﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using DomainDriver.CommonUtilities.Reflectors;

namespace DomainDriver.DomainModeling.DomainObjects
{
    public abstract class DomainObjectBase<DOMAIN_OBJECT> : IDomainObject<DOMAIN_OBJECT>
        where DOMAIN_OBJECT : class, IDomainObject<DOMAIN_OBJECT>
    {
        #region ITypedObject Implementation

        public abstract DOMAIN_OBJECT Copy();

        public abstract void CopyTo(DOMAIN_OBJECT otherObject);

        public abstract bool Equals(DOMAIN_OBJECT otherObject);

        public string GetPropertyName(Expression<Func<DOMAIN_OBJECT, object>> propertyGetter)
        {
            return ClassReflector.GetPropertyName<DOMAIN_OBJECT, object>(propertyGetter);
        }

        #endregion

        #region Object Overrides

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public override bool Equals(object obj)
        {
            if (obj == null)
            { return false; }

            Type objType = obj.GetType();
            Type thisType = typeof(DOMAIN_OBJECT);

            if (!thisType.Equals(objType))
            { return false; }

            DOMAIN_OBJECT typedObject = (DOMAIN_OBJECT)obj;
            return Equals(typedObject);
        }

        public override string ToString()
        {
            return base.ToString();
        }

        #endregion

        public void CopyDictionaryToOtherInPlace<KEY, VALUE>(DOMAIN_OBJECT otherObject, Expression<Func<VALUE, KEY>> keyGetterExpression, Expression<Func<DOMAIN_OBJECT, IDictionary<KEY, VALUE>>> dictionaryGetterExpression)
            where VALUE : DomainObjectBase<VALUE>
        {
            IDictionary<KEY, VALUE> thisDictionary = dictionaryGetterExpression.Compile()(this as DOMAIN_OBJECT);
            IDictionary<KEY, VALUE> otherDictionary = dictionaryGetterExpression.Compile()(otherObject);

            List<KEY> keysToDelete = new List<KEY>(otherDictionary.Keys);
            foreach (VALUE value in thisDictionary.Values)
            {
                KEY keyForValue = keyGetterExpression.Compile()(value);

                // Add if value not present
                if (!keysToDelete.Contains(keyForValue))
                {
                    otherDictionary.Add(keyForValue, value.Copy());
                    continue;
                }

                // Update if value not identical
                VALUE otherValue = otherDictionary[keyForValue];
                if (!value.Equals(otherValue))
                { value.CopyTo(otherValue); }

                // Record that value was found, so don't remove it
                keysToDelete.Remove(keyForValue);
            }
            foreach (KEY key in keysToDelete)
            {
                // Remove value that was NOT found
                otherDictionary.Remove(key);
            }
        }
    }
}